import Blockutils from "@/utils/BlockUtils";
import { MotaInterface } from "@/mota";
import ScrollTool from "@/mota/tools/ScrollTool";
import HeroUtils from "@/mota/utils/HeroUtils";
import { Keyboard } from "@/mota/data/KeyDataUtils";
import { ImageName } from "@/mota/data/ImageLoader";
import { ParseUnit, saveConsumablesMap, SaveItemInfo, savePropsMap } from "@/mota/data/ItemDataUnit";
import DrawUtils from "@/mota/utils/DrawlUtils";

export type SavePropInfo = [image: ImageName, info: SaveItemInfo<"props">];
export type SaveConsumablesInfo = [image: ImageName, info: SaveItemInfo<"consumables">, count: number];

export default class PackUtils implements MotaInterface {

    public hero: HeroUtils;

    public consumables: ScrollTool<SaveConsumablesInfo>;

    public props: ScrollTool<SavePropInfo>;

    public ZIndex: number = 4;

    public isOpen: boolean = false;

    public block: Blockutils = new Blockutils();

    public constructor(hero: HeroUtils) {
        this.hero = hero;

        this.consumables = new ScrollTool({
            Left: 4 + 0.4,
            Top: 4 + 0.2,
            Width: 11 - 0.4,
            Height: 3,
            Padding: 0.25,
            ItemWidth: 1.2,
            ItemHeight: 1.2,
            ItemPadding: 0.1,
            ScrollWidth: 0.1,
        });
        this.props = new ScrollTool({
            Left: 4 + 0.4,
            Top: 8 + 0.1,
            Width: 11 - 0.4,
            Height: 3,
            Padding: 0.25,
            ItemWidth: 1.2,
            ItemHeight: 1.2,
            ItemPadding: 0.1,
            ScrollWidth: 0.1,
        });
    }

    public get Children(): MotaInterface["Children"] {
        const { consumables, props } = this;
        const propList = props.List.Value.map(([, [, , info]]) => info);
        const consumablesList = consumables.List.Value.map(([, [, , info]]) => info);
        return [...propList, ...consumablesList];
    }

    public Open(): Promise<void> {
        return this.block.Wait(async () => {
            this.isOpen = true;
        }, () => {
            this.isOpen = false;
        });
    }

    public AppendPropResult(image: ImageName): string | undefined {
        const { props } = this;
        const mapInfo = savePropsMap[image];
        if (mapInfo === undefined) {
            return undefined;
        }
        const list = props.List;
        mapInfo[2].SetPack(this, "props");
        list.Value.push([image, mapInfo]);
        list.Trigger(true);
        return image;
    }

    public AppendConsumablesResult(image: ImageName, count: number = 1): string | undefined {
        const { consumables } = this;
        const mapInfo = saveConsumablesMap[image];
        if (mapInfo === undefined) {
            return undefined;
        }
        const [, , tool, unit] = mapInfo;

        tool.SetPack(this, "consumables");
        const list = consumables.List;
        const listValue = list.Value;

        const findInfo = listValue.find(info => info[0] === image);
        if (findInfo !== undefined) {
            findInfo[2] += count;
        } else {
            listValue.push([image, mapInfo, count]);
            list.Trigger(true);
        }
        return ParseUnit(image, count, unit);
    }

    public AppendProp(image: ImageName): boolean {
        const { hero: { Mota: { tip } } } = this;

        const result = this.AppendPropResult(image);
        if (result === undefined) {
            tip.AddIconMessage(image, "未知永久道具");
            return false;
        }
        tip.AddIconMessage(image, `获得${result}`);
        return true;
    }

    public AppendConsumables(image: ImageName, count: number = 1): boolean {
        const { hero: { Mota: { tip } } } = this;

        const result = this.AppendConsumablesResult(image, count);

        if (result === undefined) {
            tip.AddIconMessage(image, "未知消耗品");
            return false;
        }
        tip.AddIconMessage(image, `获得${result}`);
        return true;
    }

    public RemoveConsumables(image: ImageName) {
        const { consumables } = this;
        const list = consumables.List;
        const listValue = list.Value;
        list.Value = listValue.filter(([name]) => name !== image)
        return true;
    }

    public async DoingSome(doing: () => Promise<void>): Promise<void> {
        this.isOpen = false;
        await doing();
        this.isOpen = true;
    }

    public get SelectList() {
        const { consumables, props } = this;
        return consumables.SelectIndex !== undefined ? consumables : props;
    }

    public SelectLast(count: number = 1): void {
        const { SelectList, consumables, props } = this;
        if (SelectList.SelectIndex !== undefined) {
            SelectList.SelectLast(false, count);
            if (SelectList === props && SelectList.SelectIndex === undefined) {
                if (consumables.Length.Value > 0) {
                    consumables.SelectIndex = consumables.Length.Value - 1;
                    consumables.EnsureVisible();
                } else {
                    props.SelectIndex = props.Length.Value - 1;
                    props.EnsureVisible();
                }
            }
        } else {
            if (props.Length.Value > 0) {
                props.SelectIndex = props.Length.Value - 1;
                props.EnsureVisible();
            } else {
                consumables.SelectIndex = consumables.Length.Value - 1;
                consumables.EnsureVisible();
            }
        }
    }

    public SelectNext(count: number = 1): void {
        const { SelectList, consumables, props } = this;
        if (SelectList.SelectIndex !== undefined) {
            SelectList.SelectNext(false, count);
            if (SelectList === consumables && SelectList.SelectIndex === undefined) {
                if (props.Length.Value > 0) {
                    props.SelectIndex = 0;
                    props.EnsureVisible();
                } else {
                    consumables.SelectIndex = 0;
                    consumables.EnsureVisible();
                }
            }
        } else {
            if (consumables.Length.Value > 0) {
                consumables.SelectIndex = 0;
                consumables.EnsureVisible();
            } else {
                props.SelectIndex = 0;
                props.EnsureVisible();
            }
        }
    }

    public SelectLastRow(): void {
        this.SelectLast(this.consumables.Col.Value);
    }
    public SelectNextRow(): void {
        this.SelectNext(this.consumables.Col.Value);
    }

    public GetUtilsByKeyboard(key: Keyboard): SavePropInfo | SaveConsumablesInfo | undefined {
        const { props, consumables } = this;
        for (const utils of consumables.List.Value) {
            const [, [, keyboard]] = utils;
            if (key === keyboard) {
                return utils;
            }
        }
        for (const utils of props.List.Value) {
            const [, [, keyboard]] = utils;
            if (key === keyboard) {
                return utils;
            }
        }
        return undefined;
    }

    public async UseItem(utils: SavePropInfo | SaveConsumablesInfo) {
        const { hero: { Mota: { tip } } } = this;
        const [image, [, , tool],] = utils;
        const type = tool.itemType;
        if (type === "props") {
            await tool.Use();
        } else {
            let count = <number>utils[2];
            if (count > 0) {
                if (!await tool.Use()) {
                    return;
                }
            }
            count--;
            if (!(count > 0)) {
                this.RemoveConsumables(image);
                tip.AddIconMessage(image, `${image}已用完了`);
            } else {
                utils[2] = count;
                tip.AddIconMessage(image, `使用了${image}`);
            }
        }
    }

    public InView(): boolean {
        const { isOpen } = this;
        return isOpen;
    }

    public JudgeKey: MotaInterface["JudgeKey"] = (keyUtils): void => {
        if (!this.InView()) { return; }

        for (const key of keyUtils.keyHistory) {
            if (key === "Escape" || key === "b") {
                this.block.Release();
                keyUtils.delete(key);
            }
            else if (key === "Enter" || key === " ") {
                const SelectList = this.SelectList;
                if (SelectList !== undefined) {
                    const seleteItem = this.SelectList.SelectItem;
                    if (seleteItem !== undefined) {
                        this.DoingSome(async () => {
                            await this.UseItem(seleteItem);
                        });
                    }
                }
                keyUtils.delete(key);
            } else if (key === "ArrowUp") {
                this.SelectLastRow();
                keyUtils.delete(key);
            }
            else if (key === "ArrowDown") {
                this.SelectNextRow();
                keyUtils.delete(key);
            }
            else if (key === "ArrowLeft") {
                this.SelectLast();
                keyUtils.delete(key);
            }
            else if (key === "ArrowRight") {
                this.SelectNext();
                keyUtils.delete(key);
            }
            const utils = this.GetUtilsByKeyboard(key);
            if (utils !== undefined) {
                this.DoingSome(async () => {
                    await this.UseItem(utils);
                });
                keyUtils.delete(key);
            }
        }
    }

    public Draw: MotaInterface["Draw"] = (draw) => {
        if (!this.InView()) { return; }

        const { _2d } = draw;
        const { SelectList, consumables, props } = this;

        _2d.save();

        _2d.fillStyle = "#000000A5";
        draw.fillRect(4, 0, 11, 11);

        let consumablesKey: Keyboard | undefined = undefined;
        let propKey: Keyboard | undefined = undefined;
        if (SelectList === props) {
            if (props.SelectItem !== undefined) {
                const [image, [remark, key]] = props.SelectItem;
                propKey = key;
                this.drawRemark(draw, image, remark);
            }
        } else {
            if (consumables.SelectItem !== undefined) {
                const [image, [remark, key]] = consumables.SelectItem;
                consumablesKey = key;
                this.drawRemark(draw, image, remark);
            }
        }

        const consumablesKeyStr = !consumablesKey ? undefined : `快捷键<${consumablesKey}>`;
        this.drawTitle(draw, "消耗道具", consumablesKeyStr, 4, 4, 11, 1 / 24);
        for (const [[image, [, key], count], isSelect, [x, y, ,], [bx, by, bw, bh]] of consumables.GetItemsSize()) {
            draw.SetLineWidth(1 / 24);
            if (isSelect) {
                _2d.strokeStyle = "#00FF00";
                draw.strokeRect(bx, by, bw, bh);
            }
            draw.DrawUnti([image, true, [x, y], "run"]);

            _2d.textAlign = "right";
            _2d.textBaseline = "bottom";
            draw.SetFont(600, 1 / 2, "Mota");
            _2d.fillStyle = "#A8A81E";
            draw.fillText(key, bx + bw, by + bh);
            _2d.textBaseline = "top";
            draw.fillText(`${count}`, bx + bw, by);
        }
        {
            const scrollSize = consumables.GetScrollSize();
            if (scrollSize) {
                const [[x, y, w, h], [sx, sy, sw, sh]] = scrollSize;
                _2d.fillStyle = "#FF000044";
                draw.fillRect(x - 0.05, y - 0.05, w, h);
                _2d.fillStyle = "#445566";
                draw.fillRect(sx - 0.05, sy - 0.05, sw, sh);
            }
        }

        const propKeyStr = !propKey ? undefined : `快捷键<${propKey}>`;
        this.drawTitle(draw, "永久道具", propKeyStr, 4, 8, 11, 1 / 24);
        for (const [[image, [, key]], isSelect, [x, y, w, h], [bx, by, bw, bh]] of props.GetItemsSize()) {
            draw.SetLineWidth(1 / 24);
            if (isSelect) {
                _2d.strokeStyle = "#00FF00";
                draw.strokeRect(bx, by, bw, bh);

            }
            draw.DrawUnti([image, true, [x, y], "run"]);

            _2d.textAlign = "right";
            _2d.textBaseline = "bottom";
            draw.SetFont(600, 1 / 2, "Mota");
            _2d.fillStyle = "#A8A81E";
            draw.fillText(key, bx + bw, by + bh);

        }
        {
            const scrollSize = props.GetScrollSize();
            if (scrollSize) {
                const [[x, y, w, h], [sx, sy, sw, sh]] = scrollSize;
                _2d.fillStyle = "#FF000044";
                draw.fillRect(x - 0.05, y - 0.05, w, h);
                _2d.fillStyle = "#445566";
                draw.fillRect(sx - 0.05, sy - 0.05, sw, sh);
            }
        }

        _2d.restore();
    }

    public drawRemark(draw: DrawUtils, title: string, remark: string): void {
        const { _2d } = draw;
        _2d.textBaseline = "top";
        draw.SetFont(600, 1 / 2, "Mota");
        _2d.fillStyle = "#A8A81E";
        draw.fillText(title, 4 + 0.55, 0.45);

        draw.SetFont(400, 1 / 3, "Mota");
        _2d.fillStyle = "#FFFFFF";
        draw.fillText(remark, 4 + 0.55, 0.45 + 0.6);
    }

    public drawTitle(draw: DrawUtils, textL: string, textR: string | undefined, x: number, y: number, w: number, h: number) {
        const { _2d } = draw;
        const fontSize = 1 / 2.5;
        const padding = fontSize / 2;
        const hpadding = padding / 2;

        _2d.textAlign = "left";
        _2d.textBaseline = "top";
        draw.SetFont(600, fontSize, "Mota");
        _2d.fillStyle = "#FFFFFFBF";

        const textH = fontSize;
        const textTop = y - textH - hpadding;
        const rectH = textH + padding;

        const left = x;
        const right = x + w;
        const top = y - rectH;
        const bottomLine = y + h;

        const textLW = draw.measureText(textL);
        const rectLW = textLW + padding;
        _2d.beginPath();
        draw.moveTo(left, bottomLine);
        draw.lineTo(left, top);
        draw.lineTo(left + (rectLW), top);
        draw.lineTo(left + (rectLW + rectH), y);

        if (textR) {
            const textRW = draw.measureText(textR);
            const rectRW = textRW + padding;
            draw.lineTo(right - (rectRW + rectH), y);
            draw.lineTo(right - (rectRW), top);
            draw.lineTo(right, top);
            draw.lineTo(right, bottomLine);
            _2d.fill();

            _2d.fillStyle = "#383838";
            draw.fillText(textR, right + hpadding - rectRW, textTop);
        } else {
            draw.lineTo(right, y);
            draw.lineTo(right, bottomLine);
            _2d.fill();
        }

        _2d.fillStyle = "#383838";
        draw.fillText(textL, left + hpadding, textTop);
    }

}